# 导入相关库
import cv2
import numpy as np
import random as rng

low_value = np.array([0, 80, 0])#设阈值，去除背景部分
high_value = np.array([80, 200, 180])#表示最高值的x、y和z坐标
h_slope = 0.1#表示水平斜率
v_slope = 1.6#表示垂直斜率
zero_value = 0.001
min_distance = 15#表示最小距离
max_distance = 15#表示最小距离
min_x = 10
min_y = 10
widthA = np.sqrt(((1216 - 104) ** 2) + ((619 - 680) ** 2))
widthB = np.sqrt(((807 - 436) ** 2) + ((39 - 55) ** 2))
#width = max(int(widthA),int(widthB))
heightA = np.sqrt(((807 - 1216) ** 2) + ((619 - 39) ** 2))
heightB = np.sqrt(((436 - 104) ** 2) + ((55- 680) ** 2))
#height = max(int(heightA),int(heightB))
width = 960
height = 540

lines = []
h_result = []                                   #创建一个空列表h_result，用于存储水平直线的坐标。
v_result = []  
image_w = None
image_H = None
img = None

def cross_point(line1, line2):  # 计算交点函数
    x1 = line1[0]  # 取直线1的第一个点坐标
    y1 = line1[1]
    x2 = line1[2]  # 取直线1的第二个点坐标
    y2 = line1[3]

    x3 = line2[0]  # 取直线2的第一个点坐标
    y3 = line2[1]
    x4 = line2[2]  # 取直线2的第二个点坐标
    y4 = line2[3]

    if x2 - x1 == 0:  # L1 直线斜率不存在
        k1 = None       #y=kx+b
        b1 = 0
    else:
        k1 = (y2 - y1) * 1.0 / (x2 - x1)  # 计算k1,由于点均为整数，需要进行浮点数转化
        b1 = y1 * 1.0 - x1 * k1 * 1.0  # 整型转浮点型是关键

    if (x4 - x3) == 0:  # L2直线斜率不存在操作
        k2 = None
        b2 = 0
    else:
        k2 = (y4 - y3) * 1.0 / (x4 - x3)  # 斜率存在操作
        b2 = y3 * 1.0 - x3 * k2 * 1.0

    if k1 is None and k2 is None:  # L1与L2直线斜率都不存在，两条直线均与y轴平行
        if x1 == x3:  # 两条直线实际为同一直线
            return [x1, y1]  # 均为交点，返回任意一个点
        else:
            return None  # 平行线无交点
    elif k1 is not None and k2 is None:  # 若L2与y轴平行，L1为一般直线，交点横坐标为L2的x坐标
        x = x3
        y = k1 * x * 1.0 + b1 * 1.0
    elif k1 is None and k2 is not None:  # 若L1与y轴平行，L2为一般直线，交点横坐标为L1的x坐标
        x = x1
        y = k2 * x * 1.0 + b2 * 1.0
    else:  # 两条一般直线
        if k1 == k2:  # 两直线斜率相同
            if b1 == b2:  # 截距相同，说明两直线为同一直线，返回任一点
                return [x1, y1]
            else:  # 截距不同，两直线平行，无交点
                return None
        else:  # 两直线不平行，必然存在交点
            x = (b2 - b1) * 1.0 / (k1 - k2)
            y = k1 * x * 1.0 + b1 * 1.0
    return [x, y]

def get_distance_from_point_to_line(point, line_point1, line_point2):
    # 计算直线的三个参数  Ax+By+C=0
    A = line_point2[1] - line_point1[1]##### 计算直线的参数A。它是直线上第二个点的y坐标减去第一个点的y坐标？？？
    B = line_point1[0] - line_point2[0]##### 计算直线的参数B。它是第一个点的x坐标减去第二个点的x坐标？？？
    C = (line_point2[0] * line_point1[1]) - (line_point2[1] * line_point1[0])#计算直线的参数C。它是通过直线上的两个点计算得到的。
    # 根据点到直线的距离公式计算距离
    distance = np.abs(A * point[0] + B * point[1] + C) / (np.sqrt(A ** 2 + B ** 2) + 1e-6)
    return distance

def imageInput_birdview():
    input_image = cv2.imread('E://test//images//test_7.jpg')
    #定义输入图像中的四个顶点（透视变换前的顶点）
    bird_eye_view = np.zeros((3, 3), dtype=np.float32)
    input_points = np.float32([(643, 21), (1403, 49), (1755, 1053), (327, 955)])
    #定义输出图像中的四个顶点（透视变换后的顶点）
    output_points = np.float32([(0, 0), (width, 0), (width, height), (0, height)])
    #计算透视变换矩阵
    perspective_matrix = cv2.getPerspectiveTransform(np.array(input_points), output_points)
    #进行透视变换
    bird_eye_view = cv2.warpPerspective(input_image, perspective_matrix, (width, height))
    #cv2.imshow("bird_eye_view",bird_eye_view_cropped)
    cv2.imwrite("E://test//images//test_7s.jpg",bird_eye_view)   #使用cv2.imwrite()函数将边缘检测结果保存为图像文件


def get_imagesize_and_detect_line():
    global lines,image_w,image_H
    image_w = img.shape[1]#获取图片宽
    image_H = img.shape[0]#获取图片高
    print(image_w,'===',image_H)

    #使用cv2.inRange()函数创建一个掩膜（mask），将在指定范围内的像素设置为白色（255），其余像素设置为黑色（0）。
    #img是输入的图像，low_value和high_value是指定的最低值和最高值。
    mask = cv2.inRange(img, low_value, high_value)
    res = cv2.bitwise_and(img, img, mask=mask)       #使用cv2.bitwise_and()函数将掩膜应用于原始图像，将掩膜内的像素保留下来，其余像素设置为黑色。
    edges = cv2.Canny(mask, 150, 200)  #使用cv2.Canny()函数对掩膜图像进行边缘检测。该函数根据指定的低阈值（50）和高阈值（200）来确定边缘
    #cv2.imshow('edges', edges)                      # 使用cv2.imshow()函数显示边缘检测结果
    cv2.imwrite("E:/test/images/edges.jpg",edges)    #使用cv2.imwrite()函数将边缘检测结果保存为图像文件。
    lines = cv2.HoughLinesP(edges, 0.9, np.pi / 180, 50,minLineLength=100, maxLineGap=200)#,minLineLength=200, maxLineGap=180
    #使用cv2.HoughLinesP()函数检测图像中的直线。edges是经过边缘检测后的图像，二值图像。0.9是距离分辨率，
    #np.pi / 180是角度分辨率。100是累加平面的阈值参数，值越大，基本上意味着检出的线段越长，检出的线段个数越少。
    # minLineLength=200是最小线段长度，maxLineGap=400是最大线段间隙。
    print("检测到的直线数量",lines.shape[0])                           #打印检测到的直线数量

def devide_line(lines,h_result,v_result):
    for line in lines:                              #遍历检测到的每条直线
            x1, y1, x2, y2 = line[0]                    #从直线数组中获取直线的起点和终点坐标
            if abs(x2-x1) < zero_value:                 #判断直线的水平长度是否小于给定的阈值zero_value。如果是，则将该直线视为垂直直线，将其坐标添加到v_result列表中。
                v_result.append(line[0])        
            else:                                       #如果直线的水平长度大于等于给定的阈值。
                k = abs((y2-y1)/(x2-x1))                #计算直线的斜率
                if k < h_slope:                         #判断直线的斜率是否小于给定的水平斜率阈值h_slope。
                    h_result.append(line[0])            #如果是，则将该直线视为水平直线，将其坐标添加到h_result列表中。
                elif k > v_slope:                       #如果直线的斜率大于给定的垂直斜率阈值v_slope，则将该直线视为垂直直线，将其坐标添加到v_result列表中。
                    v_result.append(line[0])

def justice_line(type,d1,d2,min_distance,n1,n2,n11,n22):
    if type == 0:
        return ((d1 < min_distance) and (d2 < min_distance))
    elif type == 1:
        return ((d1 < min_distance) and (d2 < min_distance) or (((n1 > n11) and (n2 < n22)) or ((n2 > n22) and (n1 < n11))) and (abs(d1 - d2) < 2))
    elif type == 2:
        return (d1 < 15) or (d2 < 15)


def delete_erro_line(h_result,type):
    h_repeat=[]                                     #创建一个空列表h_repeat，用于存储重复的水平直线的索引。
    i = 0                                           #初始化变量i为0，用于迭代直线列表。
    while(True):                                    #循环，用于处理水平直线
        x1, y1, x2, y2 = h_result[i]                #从水平直线列表中获取当前直线的起点和终点坐标。
        h_repeat.clear()                            #清空重复直线索引列表
        length = len(h_result)                      #获取水平直线列表的长度。
        if i < length :                          #如果当前直线不是最后一条直线
            for j in range(i + 1,length, 1):                                                #遍历当前直线之后的每条直线
                x1_temp, y1_temp, x2_temp, y2_temp = h_result[j]                            #从水平直线列表中获取当前遍历的直线的起点和终点坐标。
                point1 = np.array([x1, y1])                                                 #创建一个包含线段第一个点坐标的numpy数组point1
                point2 = np.array([x2, y2])                                                 #创建一个包含线段第二个点坐标的numpy数组point2
                line_point1 = np.array([x1_temp, y1_temp])                                  #创建一个包含垂直线段起点坐标的numpy数组 
                line_point2 = np.array([x2_temp, y2_temp])                                  #创建一个包含垂直线段终点坐标的numpy数组。
                d1 = get_distance_from_point_to_line(point1, line_point1, line_point2)      #调用get_distance_from_point_to_line函数，计算点到直线的距离。
                d2 = get_distance_from_point_to_line(point2, line_point1, line_point2)      #调用get_distance_from_point_to_line函数，计算点到直线的距离。
                if justice_line(type,d1,d2,min_distance,y1,y2,y1_temp,y2_temp):
                #if (d1 < min_distance) and (d2 < min_distance) or (((y1 > y1_temp) and (y2 < y2_temp)) or ((y2 > y2_temp) and (y1 < y1_temp))) and (abs(d1 - d2) < 2):                             #如果两个距离都小于最小距离。             
                    h_repeat.append(j)                                                      #将当前垂直线段的索引添加到h_repeat列表中
            counter = 0                             #创建一个计数器变量counter并初始化为0。
            for index in h_repeat:                  #遍历h_repeat列表中的每个索引
                index = index - counter             #更新索引值，减去计数器的值
                h_result.pop(index)                 #从h_result列表中移除具有指定索引的元素
                counter += 1                        #增加计数器的值 
        i += 1                                      #增加i的值
        if i >= len(h_result):                      #如果i大于等于h_result列表的长度
            break
def  refresh_HOR_point(h_result):
    for i in range(len(h_result)):                                  #遍历h_result列表中的每个索引,计算出水平线上两个点的坐标
            x1, y1, x2, y2 = h_result[i]                                #从h_result列表中获取水平线段的起点和终点坐标
            k = (y2 - y1) / (x2 - x1)                                   #计算水平线段的斜率
            x0 = 0                                                      #将直线的起点横坐标设置为0
            y0 = (x0-x1) * k + y1                                       #根据斜率和起点坐标计算直线的纵坐标。
            h_result[i][0] = x0                                         #更新水平线段的起点横坐标。
            h_result[i][1] = y0                                         #更新水平线段的起点纵坐标

            x0 = image_w-1                                              #将直线的终点横坐标设置为图像宽度减1
            y0 = (x0 - x1) * k + y1                                     #根据斜率和起点坐标计算直线的纵坐标
            h_result[i][2] = x0                                         #更新水平线段的终点横坐标
            h_result[i][3] = y0                                         #更新水平线段的终点纵坐标

def refresh_vert_point(v_result):
    for i in range(len(v_result)):                                  #遍历v_result列表中的每个索引,计算出垂直线上两个点的坐标
        x1, y1, x2, y2 = v_result[i]                                #从v_result列表中获取垂直线段的起点和终点坐标
        if abs(x2 - x1) < zero_value:                               #如果垂直线段的起点和终点横坐标之差小于zero_value
            v_result[i][1] = 0                                      #将垂直线段的起点纵坐标设置为0
            v_result[i][3] = image_H                                #将垂直线段的终点纵坐标设置为图像高度
        else:
            k = (y2 - y1) / (x2 - x1)                               #计算垂直线段的斜率
            y0=0                                                    #将直线的起点纵坐标设置为0
            x0=(y0-y1)/k + x1                                       #根据斜率和起点坐标计算直线的横坐标
            v_result[i][0] = x0                                     #更新垂直线段的起点横坐标
            v_result[i][1] = y0                                     #更新垂直线段的起点纵坐标

            y0 = image_H                                            #将直线的终点纵坐标设置为图像高度
            x0 = (y0 - y1) / k + x1                                 #根据斜率和起点坐标计算直线的横坐标
            v_result[i][2] = x0                                     #更新垂直线段的终点横坐标
            v_result[i][3] = y0                                     #更新垂直线段的终点纵坐标

def draw_line(h_result,R,G,B,linesize):
    for line in h_result:                                           #遍历h_result列表中的每个元素
        x1, y1, x2, y2 = line                                       #从h_result列表中获取水平线段的起点和终点坐标
        cv2.line(img, (x1, y1), (x2, y2), (R, G, B), linesize, lineType=cv2.LINE_AA) #在图像上绘制水平线段

def average_line(x1,x2):
    x1 = (int)((x1 + x2)/2)
    return x1

def draw_Crossing_point(h_center,v_center):
    postion = []
    postion.clear()
    for line1 in h_center:                                          ## 遍历水平线段列表
        x1, y1, x2, y2 = line1
        line1_temp = np.array([x1, y1, x2, y2])
        for line2 in v_center:                                      ## 遍历垂直线段列表
            x1_temp, y1_temp, x2_temp, y2_temp = line2
            line2_temp = np.array([x1_temp, y1_temp, x2_temp, y2_temp])
            x,y = cross_point(line1_temp, line2_temp)
            x = (int)(x)
            y = (int)(y)
            #print(x,"==",y)
            center_coordinates = ((int)(x),(int)(y))
            data1 = [x*2,y*2]
            postion.append(data1)
            cv2.circle(img, center_coordinates, 15, (255, 0, 255),3)## 在交点处绘制圆形标记

if __name__ == '__main__':
    imageInput_birdview()
    img = cv2.imdecode(np.fromfile(r"E://test//images//test_7s.jpg", dtype=np.uint8), -1)#读取文件夹中的图片
    get_imagesize_and_detect_line()
    if img is not None:
        devide_line(lines,h_result,v_result)
        print("分类后h_result",len(h_result))
        print("分类后v_result",len(v_result))
        delete_erro_line(h_result,1)
        delete_erro_line(v_result,0)
        print("/***********************************************/")      
        print("直线h_result",len(h_result))
        print("直线v_result",len(v_result))
        refresh_HOR_point(h_result)
        refresh_vert_point(v_result)
        draw_line(h_result,0,255,0,1)
        draw_line(v_result,255,255,255,1)
        print("/***********************************************/")      
        print("最终的直线h_result",len(h_result))
        print("最终的直线v_result",len(v_result))
        h_result.sort(key=lambda x:x[1])                                #根据水平线段的起点纵坐标对h_result列表进行排序
        v_result.sort(key=lambda x: x[0]) 
        h_center = []                                                   #创建一个空列表h_center
        for i in range(len(h_result)-1):                                #遍历h_result列表中的每个索引
            x1, y1, x2, y2 = h_result[i]                                #从h_result列表中获取当前水平线段的起点和终点坐标
            x1_temp, y1_temp, x2_temp, y2_temp = h_result[i+1]          #从h_result列表中获取下一个水平线段的起点和终点坐标
            d1 = y1_temp - y1                                            #计算两个水平线段之间的纵向距离
            d2 = y2_temp - y2
            #if ((h_result[i][1] > image_H/2)) and (d1 < 50) and (abs(d1 - d2) < 30):                                                 #如果纵向距离小于100
            if d1 < 50:  
                x1 = average_line(x1,x1_temp)
                y1 = average_line(y1,y1_temp)
                x2 = average_line(x2,x2_temp)
                y2 = average_line(y2,y2_temp)
                newline = [x1, y1, x2, y2]                              #创建一个包含新水平线段坐标的列表newline
                h_center.append(newline)                                #将新水平线段添加到h_center列表中

        v_center = []                                                   #创建一个空列表v_center，用于存储垂直线段的中心点坐标
        for i in range(len(v_result) - 1):                              #遍历垂直线段列表v_result中的每一个线段
            x1, y1, x2, y2 = v_result[i]                                #
            x1_temp, y1_temp, x2_temp, y2_temp = v_result[i + 1]        #获取当前线段的起点和终点坐标
            d = x1_temp - x1                                            #获取下一个线段的起点和终点坐标
            if d < 40:                                                 #如果横向距离小于100
                x1 = average_line(x1,x1_temp)
                y1 = average_line(y1,y1_temp)
                x2 = average_line(x2,x2_temp)
                y2 = average_line(y2,y2_temp)
                newline = [x1, y1,x2, y2]                               #创建一个包含新垂直线段坐标的列表
                v_center.append(newline)                                #将新垂直线段添加到v_center列表中

        h_center.sort(key=lambda x:x[1])                                #根据水平线段的起点纵坐标对h_result列表进行排序
        v_center.sort(key=lambda x:x[0])                                #根据水平线段的起点纵坐标对v_result列表进行排序
        delete_erro_line(h_center,2)
        delete_erro_line(v_center,2)
        draw_line(h_center,255,255,255,3)
        draw_line(v_center,0,0,0,3)
        print(h_center)
        print(v_center) 
        draw_Crossing_point(h_center,v_center)
        cv2.imshow('img', img)
        cv2.imwrite("E:/test/images/new_test.jpg",img)
        print("OK")
    cv2.waitKey(0)  
